/* 
 * File:   Aritmetico.h
 * Author: marce,nico
 *
 * Created on 8 de junio de 2012
 */

#ifndef ARITMETICO_H
#define ARITMETICO_H

#include "includes.h"
//#include "BitFileManager.cpp"
#include "Aritmetico.h"

/* Metodos publicos */

/* public */ void Aritmetico::setInputFilename(const char* filename){
	this->inputFilename = (char*)filename;
}

/* public */ void Aritmetico::setOutputFilename(const char* filename){
	this->outputFilename = (char*)filename;
}

AritmeticoCompress::AritmeticoCompress(bool useAllASCII=true){
	this->readingFromFile = false;
	this->imAtFirstBlock = false;
	
	this->contador_underflow = 0;
	this->contador_last_underflow = 0;
	this->contador_frecuencias = 0;
	this->contador_bits_emitidos = 0;
	
	this->buffer_byte_a_emitir = 0x00;
	
	if(useAllASCII){
		CharFrec tupla;
		for( short cont=0 ; cont<=255 ; cont++ ){
			tupla.caracter   = (char)cont;
			tupla.frecuencia = 1;
			this->intervalo.tblFrecuencias.push_back(tupla);
			this->contador_frecuencias++;
		}
		this->intervalo.techo = 255;
		this->intervalo.piso = 0;
	}
	
	this->debug = true;
}

AritmeticoCompress::AritmeticoCompress(const char* inputFilename , const uInt blockSize , bool useAllASCII=true ){
	this->setInputFilename(inputFilename);
	this->setOutputFilename("Aritmetico.output.txt");
	this->blockSize = blockSize;
	this->readingFromFile = true;
	this->imAtFirstBlock = true;
	
	this->contador_underflow = 0;
	this->contador_last_underflow = 0;
	this->contador_frecuencias = 0;
	this->contador_bits_emitidos = 0;
	
	this->buffer_byte_a_emitir = 0x00;
	
	if(useAllASCII){
		CharFrec tupla;
		for( short cont=0 ; cont<=255 ; cont++ ){
			tupla.caracter   = (char)cont;
			tupla.frecuencia = 1;
			this->intervalo.tblFrecuencias.push_back(tupla);
			this->contador_frecuencias++;
		}
		this->intervalo.techo = 255;
		this->intervalo.piso = 0;
	}
	
	this->debug = true;
}

AritmeticoCompress::~AritmeticoCompress(){
	if( this->readingFromFile && !this->imAtFirstBlock){
		this->outputFile->close();
		delete this->outputFile;
	}
}

void AritmeticoCompress::emitirBit(bool bit){
	
	if(this->debug){
		if( bit == 1 ){
			cout << "emitirBit: Emito un 1" << endl;
		}else if( bit == 0 ){
			cout << "emitirBit: Emito un 0" << endl;
		}
	}
	
	this->contador_bits_emitidos++;
	
	//cout << "emitirBit: contador_bits_emitidos: " << (int)contador_bits_emitidos << endl;
	
	this->buffer_byte_a_emitir <<= 1;
	
	if( bit == 1 ){
		this->buffer_byte_a_emitir |= 0x01;
	}
	
	if( this->contador_bits_emitidos == 8 ){
		this->salidaComprimida.push_back( this->buffer_byte_a_emitir );
		this->contador_bits_emitidos = 0;
		this->buffer_byte_a_emitir = 0x00;
	}
}

/* private */ int AritmeticoCompress::resolverOverflow(){
	uChar techo = this->intervalo.techo;
	uChar piso  = this->intervalo.piso;

	if(this->debug) cout << "resolverOverflow: techo: " << (int)techo << endl;
	if(this->debug) cout << "resolverOverflow: piso: " << (int)piso << endl;

	uChar nuevoTecho = techo;
	uChar nuevoPiso  = piso;

	uChar aux = ~(techo ^ piso);

	uChar mask = 0x80;

	int cont = 0;
	
	bool primer_bit;
	
	for( uChar offset = 1 ; offset <= 8 ; offset++ ){
		if( aux == (aux | mask) ){
			if( techo == (techo | mask) ){
				this->emitirBit(1);
				primer_bit = 1;
			}else{
				this->emitirBit(0);
				primer_bit = 0;
			}

			if( offset == 1 ){
				int cont2 = 0 ;
				cout << "this->contador_last_underflow: " << (int)this->contador_last_underflow << endl;
				while( cont2 < this->contador_last_underflow ){
					this->emitirBit(!primer_bit);
					cont2++;
				}
				this->contador_last_underflow = 0;
			}

			cont++;
		}else{
			break;
		}

		mask >>= 1;

	}
	
	for( int aux = 0 ; aux < cont ; aux++ ){
		nuevoTecho <<= 1;
		nuevoTecho |= 0x01;
		
		nuevoPiso <<= 1;
	}

	this->intervalo.techo = nuevoTecho;
	this->intervalo.piso  = nuevoPiso;

	if(this->debug) cout << "resolverOverflow: nuevo techo: " << (int)this->intervalo.techo << endl;
	if(this->debug) cout << "resolverOverflow: nuevo piso: " << (int)this->intervalo.piso << endl;

	return 0;
}


/* private */ bool AritmeticoCompress::hayUnderflow(char techo,char piso){
	if( ( techo == (techo | 0x80) ) and ( ~piso == (~piso | 0x80) ) ){
		if( ( ~techo == (~techo | 0x40) ) and ( piso == (piso | 0x40) ) ){
			return true;
		}
	}
	return false;
}

/* private */ int AritmeticoCompress::resolverUnderflow(){
	char techo = this->intervalo.techo;
	char piso  = this->intervalo.piso;
	
	
	if( !this->hayUnderflow(techo,piso) ) return 0;
	uChar cont = 1;
	
	char nuevo_techo = 0x1;
	char nuevo_piso  = 0x0;
	
	// Cuento la cantidad de underflow 
	char mask = 0b00100000;
	char offset;
	for( offset = 3 ; offset<9 ; offset++ ){
		if( ( ~techo == (~techo | mask) ) and ( piso == (piso | mask) ) ){
			cont++;
		}else{
			break;
		}
		mask >>= 1;
	}
	this->contador_last_underflow += cont;
	this->contador_underflow += cont;
	
	if(this->debug) {
		cout << "resolverUnderflow: offset: " << (int)offset << endl;
		cout << "resolverUnderflow: Cantidad de underflow: " << (int)cont << endl;
		cout << "resolverUnderflow: Cantidad de underflow acumulado: " << (int)this->contador_underflow << endl;
	}
	
	// Agrego, si los hay, los bits que no dieron 
	// underflow al nuevo techo y nuevo piso
	while( offset++ < 9 ){
		nuevo_techo <<= 1;
		if( techo == (techo | mask) ){
			nuevo_techo |= 0x01;
		}
		
		nuevo_piso <<= 1;
		if( piso == (piso | mask) ){
			nuevo_piso |= 0x01;
		}
		
		mask >>= 1;
	}
	
	// Agrego el padding para el techo (con bits en 1) y
	// para el piso (con bits en 0)
	while( cont-- > 0 ){
		
		nuevo_techo <<= 1;
		nuevo_techo |= 0x01;
		
		nuevo_piso <<= 1;
	}
	
	this->intervalo.techo = nuevo_techo;
	this->intervalo.piso  = nuevo_piso;
	
	if(this->debug){
		cout << "resolverUnderflow: nuevo techo: " << (int)this->intervalo.techo << endl;
		cout << "resolverUnderflow: nuevo piso: " << (int)this->intervalo.piso << endl;
	}
	
	return 0;
}

/* public */ void AritmeticoCompress::setTablaChars( vector<char> vectorChars ){
	vector<char>::iterator it;
	CharFrec tupla;
	for( it = vectorChars.begin() ; it < vectorChars.end() ; it++ ){
		tupla.caracter   = *it;
		tupla.frecuencia = 1;
		this->intervalo.tblFrecuencias.push_back(tupla);
		this->contador_frecuencias++; 
	}

	this->intervalo.techo = 255;
	this->intervalo.piso = 0;
}

/* public */ int AritmeticoCompress::compressChar(const char caracter , bool is_last_char = false){


	if(this->debug) cout << endl << "Comprimiendo char: '"<< caracter <<"' (ASCII: " << (int)caracter << ")\n";
	
	if(this->debug) cout << "Techo: " << (int)this->intervalo.techo << endl;
	if(this->debug) cout << "Piso: " << (int)this->intervalo.piso << endl;
	
	unsigned short rango = this->intervalo.techo - this->intervalo.piso + 1;
	
	if(this->debug) cout << "Rango: "<<rango<<endl;
	
	this->intervalo.techo = (uChar)(this->intervalo.piso - 1 + (rango*this->calcularTecho(caracter)) );
	if(this->debug) cout << "Nuevo Techo: " << (int)this->intervalo.techo << endl;
	
	this->intervalo.piso = (uChar)(this->intervalo.piso + (rango*this->calcularPiso(caracter)) );
	if(this->debug) cout << "Nuevo Piso: " << (int)this->intervalo.piso << endl;
	
	if( !is_last_char ){
		this->resolverOverflow();
		this->resolverUnderflow();

		this->actualizarTblFrecuencias(caracter);
	}else{
		this->resolverUltimoChar(caracter);
	}
	
	return 0;
}

/* private */ int AritmeticoCompress::resolverUltimoChar(char caracter){
	// El ultimo char se resuelve emitiendo el piso como lo hace el overflow.
	// No puedo usar el metodo resolverOverflow() pues este usa el techo y 
	// yo necesito emitir piso.
	
	
	uChar piso  = this->intervalo.piso;
	

	uChar mask = 0x80;

	int cont = 0;
	
	bool primer_bit;
	
	for( uChar offset = 1 ; offset <= 8 ; offset++ ){

		if( piso == (piso | mask) ){
			this->emitirBit(1);
			primer_bit = 1;
		}else{
			this->emitirBit(0);
			primer_bit = 0;
		}

		if( offset == 1 ){
			int cont2 = 0;
			while( cont2 < this->contador_last_underflow ){
				this->emitirBit(!primer_bit);
				cont2++;
			}
		}

		cont++;

		mask >>= 1;

	}
	
	return 0;
}

int AritmeticoCompress::getSalidaComprimida(string* salidaComprimida , bool completarAbyte = false){
	
	
	uChar it = this->contador_bits_emitidos;
	
	if(completarAbyte){
		if(this->debug) cout << "Aplicando padding.." << endl;
		
		this->emitirBit(1);
		if( it == 0 ){
			for( it=1 ; it<8 ; it++ ){
				this->emitirBit(0);
			}
		}else{
			it = this->contador_bits_emitidos + 1 ;
			while(it<=8){
				this->emitirBit(0);
				it++;
			}
		}
	}
	
	if(this->readingFromFile){
		*(this->outputFile) << this->salidaComprimida;
	}
	
	salidaComprimida->clear();
	salidaComprimida->assign( this->salidaComprimida );
	
	this->salidaComprimida.clear();
	
	return  0;
}

/* public */ int AritmeticoCompress::runBlock(const char* texto , string* salidaComprimida , bool isEOF = false , bool completarAbyte = false){
	
	char* iterador = (char*)texto;
	uInt cont=0;

	if( this->readingFromFile == false ){
		this->lastReadSize = strlen(texto);
	}
	while( cont++ < this->lastReadSize ){
		this->compressChar( *iterador );
		iterador++;
	}
	
	return this->getSalidaComprimida(salidaComprimida , completarAbyte );
	
}


/* public */ int AritmeticoCompress::runNextBlock( string* salidaComprimida ){
	if( this->imAtFirstBlock == true ){
		this->imAtFirstBlock = false;
		this->outputFile = new ofstream( this->outputFilename  );
	}
	uInt status;
	char* texto = (char*)malloc( this->blockSize * sizeof(char) );

	status = this->getNextBlockOfText(&texto);

	if( status == ERROR_LECTURA ){
		//cout << "Error de lectura." << endl;
		free(texto);
		return ERROR_LECTURA;

	}else if( status == ERROR_EOF ){
		//cout << "End of file." << endl;
		free(texto);
		return ERROR_EOF;
	}

	this->runBlock( texto , salidaComprimida );
	
	//cout << "Escribiendo archivo " << this->outputFilename << endl;
	//this->outputFile->writeAritmeticResult( salidaComprimida );
	
	free(texto);
	
	return 0;
}

int AritmeticoCompress::run(){
	string texto;
	
	while( ERROR_EOF != this->runNextBlock( &texto ) );
	
	texto.clear();
	
	this->getSalidaComprimida( &texto , true );
	
	return 0;
}

// DEBUG
void AritmeticoCompress::mostrarTabla(){
	vector<CharFrec>::iterator it;
	for( it = this->intervalo.tblFrecuencias.begin() ; it < this->intervalo.tblFrecuencias.end() ; it++ ){
		printf( "Char: %3d ; Frec: %d\n" , (uChar)it->caracter , it->frecuencia);
	}
}

/* private */ float AritmeticoCompress::calcularTecho(char caracter){
	uInt cont_frec = 0;
	
	vector<CharFrec>::iterator it;
	for( it = this->intervalo.tblFrecuencias.begin() ; it < this->intervalo.tblFrecuencias.end() ; it++ ){
		cont_frec += it->frecuencia;
		if(it->caracter == caracter) break;
	}
	
	return ( (float)cont_frec / (float)this->contador_frecuencias );
}

/* private */ float AritmeticoCompress::calcularPiso(char caracter){
	uInt cont_frec = 0;
	
	vector<CharFrec>::iterator it;
	for( it = this->intervalo.tblFrecuencias.begin() ; it < this->intervalo.tblFrecuencias.end() ; it++ ){
		if(it->caracter == caracter) break;
		cont_frec += it->frecuencia;
	}
	
	return ( (float)cont_frec / (float)this->contador_frecuencias );
}

/* private */ int AritmeticoCompress::actualizarTblFrecuencias(char caracter){
	
	vector<CharFrec>::iterator it;
	for( it = this->intervalo.tblFrecuencias.begin() ; it < this->intervalo.tblFrecuencias.end() ; it++ ){
		if( it->caracter == caracter ){
			it->frecuencia++;
			break;
		}
	}
	
	this->contador_frecuencias++;
	
	return 0;
}

/* private */ int AritmeticoCompress::getNextBlockOfText(char** text){
	if( ! this->inputFile.is_open() ){
		this->inputFile.open( this->inputFilename , ios::binary );
		if( ! this->inputFile.is_open() ) {
			return ERROR_FILE_NOT_EXIST;
		}
		this->readingFromFile = true;
	}

	if( this->inputFile.eof() ){
		//cout << "Error de lectura." << endl;
		return ERROR_EOF;
	}
	
	//this->inputFile.seekg(this->readSize);
	this->inputFile.read( *text , this->blockSize );
	
	this->lastReadSize =  this->inputFile.gcount();
	

	if( this->inputFile.bad() ){
		//cout << "Error de lectura." << endl;
		return ERROR_LECTURA;
	}
	
	return 0;
}














AritmeticoDecompress::AritmeticoDecompress(bool useAllASCII=true){
	this->readingFromFile = false;
	this->imAtFirstBlock = false;
	
	this->contador_underflow = 0;
	this->contador_overflow = 0;
	this->contador_frecuencias = 0;
	this->contador_bits_corridos = 0;
	
	//this->buffer_byte_a_emitir = 0x00;
	
	if(useAllASCII){
		CharFrec tupla;
		for( short cont=0 ; cont<=255 ; cont++ ){
			tupla.caracter   = (char)cont;
			tupla.frecuencia = 1;
			this->intervalo.tblFrecuencias.push_back(tupla);
			this->contador_frecuencias++;
		}
		this->intervalo.techo = 255;
		this->intervalo.piso = 0;
	}
	
	this->debug = true;
}

/*
 * No es necesario por el modelo estructurado. No hay tiempo para hacer esto.
 * 
AritmeticoDecompress::AritmeticoDecompress(const char* inputFilename , const uInt blockSize , bool useAllASCII=true ){
	this->setInputFilename(inputFilename);
	this->setOutputFilename("Aritmetico.decompress.output.txt");
	this->blockSize = blockSize;
	this->readingFromFile = true;
	this->imAtFirstBlock = true;
	
	this->contador_underflow = 0;
	this->contador_overflow = 0;
	this->contador_frecuencias = 0;
	this->contador_bits_corridos = 0;
	//this->contador_bits_emitidos = 0;
	
	//this->buffer_byte_a_emitir = 0x00;
	
	if(useAllASCII){
		CharFrec tupla;
		for( short cont=0 ; cont<=255 ; cont++ ){
			tupla.caracter   = (char)cont;
			tupla.frecuencia = 1;
			this->intervalo.tblFrecuencias.push_back(tupla);
			this->contador_frecuencias++;
		}
	}
	
	this->intervalo.techo = 255;
	this->intervalo.piso = 0;
	
	this->debug = true;
}
*/

AritmeticoDecompress::~AritmeticoDecompress(){
	if( this->readingFromFile && !this->imAtFirstBlock){
		this->outputFile->close();
		delete this->outputFile;
	}
}

int AritmeticoDecompress::decompressString(const string* bytes , string* salidaDescomprimida ){
	
	if( bytes->length() < 2 ) return -1;
	
	this->tira_de_bytes = (string*)bytes;
	
	this->salidaDescomprimida = salidaDescomprimida;
	this->salidaDescomprimida->clear();
	
	this->char_actual = (uChar)this->tira_de_bytes->at(0);
	this->buffer_next_byte = (uChar)this->tira_de_bytes->at(1);
	
	this->buffer_next_byte_index = 2;
	
	int iteracion = 1;
	
	cout << "Arrancando descompresion... techo: " << (int)this->intervalo.techo << " ; piso: " << (int)this->intervalo.piso << endl;
	
	//while( this->buffer_next_byte_index < bytes->length() ){
	while(true){
		
		cout << "Iteracion: " << iteracion << endl; iteracion++;
		
		this->resolverOverflow();
		this->resolverUnderflow();
	
		// Hago los corrimientos para overflow
		this->overflow_shift();
		
		// Hago los corrimientos para underflow
		this->underflow_shift();
		
		// Hace una busqueda binaria hasta encontrar el rango donde se encuentra
		// techo y piso actual, y emite el caracter de ese rango
		this->calcular_techo_y_piso();
		
		// Muevo los 8 bits de buffer_next_byte a char_actual y leo un byte mas.
		//this->leer_bits(8);
		
		cout << "this->buffer_next_byte_index: " << (int)this->buffer_next_byte_index << endl;
		cout << "this->buffer_next_byte: " << (int)this->buffer_next_byte << endl;
		cout << "bytes->length(): " << (int)bytes->length() << endl;
		
		if( (this->buffer_next_byte_index == bytes->length()) and (this->buffer_next_byte == 0x80) ){
			cout << "Break OK!" << endl;
			break;
		}
		
		
		
		if( iteracion == 400 ){
			cout << "ITERACION 400" << endl;
			break;
		}
		
	}
	
	printf("Char actual (HEXA): %02X\n" , this->char_actual );
	printf("Buffer next byte (HEXA): %02X\n" , this->buffer_next_byte );
	
	return 0;
}

int AritmeticoDecompress::leer_bits(int bits){
	
	uChar nuevo_char_actual = this->char_actual;
	uChar nuevo_buffer = this->buffer_next_byte;
	
	for(int i=0 ; i < bits ; i++ ){
		
		nuevo_char_actual <<= 1;
		if( nuevo_buffer == (nuevo_buffer | 0x80) ){
			nuevo_char_actual |= 0x01;
		}
		nuevo_buffer <<= 1;
		
		this->contador_bits_corridos++;
		
		if( this->contador_bits_corridos == 8 ){
			if(this->buffer_next_byte_index < this->tira_de_bytes->length()){
				this->buffer_next_byte = (uChar)this->tira_de_bytes->at(this->buffer_next_byte_index);
				this->buffer_next_byte_index++;
				this->contador_bits_corridos = 0;
				
				nuevo_buffer = this->buffer_next_byte;
			}
		}
		
	}
	
	this->char_actual = nuevo_char_actual;
	this->buffer_next_byte = nuevo_buffer;
	
	return 0;
}

int AritmeticoDecompress::calcular_techo_y_piso(){
	unsigned short rango = this->intervalo.techo - this->intervalo.piso + 1;
	
	uInt cont_frec = 0;
	uInt cont_frec_anterior = 0;
	float techo_char;
	float piso_char;
	uChar techo_aux;
	uChar piso_aux;
	
	vector<CharFrec>::iterator it;
	for( it = this->intervalo.tblFrecuencias.begin() ; it < this->intervalo.tblFrecuencias.end() ; it++ ){
		
		cont_frec += it->frecuencia; // Para el calculo de techo
		cont_frec_anterior = cont_frec - it->frecuencia; // Para el calculo de piso
		
		piso_char = ( (float)cont_frec_anterior / (float)this->contador_frecuencias );
		piso_aux = (uChar)(this->intervalo.piso + (rango*piso_char) );
		
		techo_char = ( (float)cont_frec / (float)this->contador_frecuencias );
		techo_aux = (uChar)(this->intervalo.piso - 1 + (rango*techo_char) );
		
		if( (this->char_actual <= techo_aux) and (this->char_actual >= piso_aux) ){
			
			cout << "calcular_techo_y_piso: char_actual: " << (int)this->char_actual << endl;
			
			// Emito el char
			this->emitirChar( it->caracter );
			
			/* Como tengo todos los datos aca, calculo el nuevo techo y nuevo piso
			 * para el proximo char a descomprimir 
			 */
			
			// Calculo nuevo piso
			piso_char = ( (float)cont_frec_anterior / (float)this->contador_frecuencias );
			piso_aux = (uChar)(this->intervalo.piso + (rango*piso_char) );
			
			// Calculo nuevo techo
			techo_char = ( (float)(cont_frec) / (float)this->contador_frecuencias );
			techo_aux = (uChar)(this->intervalo.piso - 1 + (rango*techo_char) );
			
			cout << "Techo: " << (int)this->intervalo.piso << " - 1 + (" << int(rango) << "*" << (int)techo_char << ")" << endl;
			
			this->intervalo.piso = piso_aux;
			this->intervalo.techo = techo_aux;
			
			/*
			cout << "this->contador_frecuencias: " << this->contador_frecuencias << endl;
			cout << "Rango: " << rango << endl;
			cout << "Nuevo techo: " << (int)this->intervalo.piso << " - 1 + " << rango << "*" << techo_char << endl;
			cout << "Techo de " << it->caracter << ": " << techo_char << endl;
			cout << "Nuevo techo: " << (int)techo_aux << endl;
			*/
			
			// Actualizo la frecuencia del char emitido
			it->frecuencia++;
			
			// Actualizo el contador general de frecuencias
			this->contador_frecuencias++;
			
			break;
		}
	}
	
	return 0;
}

int AritmeticoDecompress::emitirChar(char caracter){
	
	if(this->debug) cout << "Emito char: '" << caracter << "' (ASCII: " << (int)caracter << ")" << endl << endl;
	
	*this->salidaDescomprimida += caracter;
	
	return 0;
}

int AritmeticoDecompress::overflow_shift(){
	
	this->leer_bits(this->contador_overflow);
	
	return 0;
}

int AritmeticoDecompress::underflow_shift(){
	
	if(this->contador_underflow == 0) return 0;
	
	printf("underflow_shift: char_actual antes de los corrimientos (HEXA): %02X\n",this->char_actual);
	
	uChar nuevo_char_actual = 0x00;
	uChar mask = 0x80;
	
	// El primer bit queda igual
	if( this->char_actual == (this->char_actual | mask ) ){
		nuevo_char_actual = 0x01;
	}
	
	uChar cont_underflow = this->contador_underflow;
	
	// Me salteo los siguientes N bits, y copio los restantes
	for( uChar offset = 2 ; offset <= 8 ; offset++ ){

		mask >>= 1;
		if(cont_underflow > 0){
			cont_underflow--;
			continue;
		}
		
		nuevo_char_actual <<= 1;
		if( this->char_actual == (this->char_actual | mask) ){
			nuevo_char_actual |= 0x01;
		}
	}
	
	// Relleno los N bits que quite con los primeros N bits de buffer_next_bytes
	this->char_actual = nuevo_char_actual;
	this->leer_bits(this->contador_underflow);
	/*
	for( uChar cont = 0 ; cont < this->contador_underflow ; cont++ ){
		nuevo_char_actual <<= 1;
		if( this->buffer_next_byte == (this->buffer_next_byte | 0x80) ){
			nuevo_char_actual |= 0x01;
		}
		this->buffer_next_byte <<= 1;
		this->contador_bits_corridos++;
		
		if( this->contador_bits_corridos == 8 ){
			this->buffer_next_byte = (uChar)this->tira_de_bytes->at(this->buffer_next_byte_index);
			this->buffer_next_byte_index++;
			this->contador_bits_corridos = 0;
		}
	}
	*/
	
	//this->char_actual = nuevo_char_actual;
	
	this->contador_underflow = 0;
	
	printf("underflow_shift: char_actual despues de los corrimientos (HEXA): %02X\n",this->char_actual);
	
	return 0;
}

int AritmeticoDecompress::resolverOverflow(){
	uChar techo = this->intervalo.techo;
	uChar piso  = this->intervalo.piso;

	if(this->debug) cout << "resolverOverflow: techo: " << (int)techo << endl;
	if(this->debug) cout << "resolverOverflow: piso: " << (int)piso << endl;

	uChar nuevoTecho = techo;
	uChar nuevoPiso  = piso;

	uChar aux = ~(techo ^ piso);

	uChar mask = 0x80;

	int cont = 0;
	
	bool primer_bit;
	
	for( uChar offset = 1 ; offset <= 8 ; offset++ ){
		if( aux == (aux | mask) ){
			if( techo == (techo | mask) ){
				//this->emitirBit(1);
				primer_bit = 1;
			}else{
				//this->emitirBit(0);
				primer_bit = 0;
			}

			cont++;
		}else{
			break;
		}

		mask >>= 1;
	}
	
	for( int aux = 0 ; aux < cont ; aux++ ){
		nuevoTecho <<= 1;
		nuevoTecho |= 0x01;
		
		nuevoPiso <<= 1;
	}

	this->intervalo.techo = nuevoTecho;
	this->intervalo.piso  = nuevoPiso;

	this->contador_overflow = cont;
	
	if(this->debug) cout << "resolverOverflow: contador_overflow: " << (int)this->contador_overflow << endl;

	if(this->debug) cout << "resolverOverflow: nuevo techo: " << (int)this->intervalo.techo << endl;
	if(this->debug) cout << "resolverOverflow: nuevo piso: " << (int)this->intervalo.piso << endl;

	return 0;
}


bool AritmeticoDecompress::hayUnderflow(uChar techo,uChar piso){
	if( ( techo == (techo | 0x80) ) and ( ~piso == (~piso | 0x80) ) ){
		if( ( ~techo == (~techo | 0x40) ) and ( piso == (piso | 0x40) ) ){
			return true;
		}
	}
	return false;
}

int AritmeticoDecompress::resolverUnderflow(){
	uChar techo = this->intervalo.techo;
	uChar piso  = this->intervalo.piso;
	
	if(this->debug) cout << "resolverUnderflow: techo: " << (int)techo << endl;
	if(this->debug) cout << "resolverUnderflow: piso: " << (int)piso << endl;
	
	if( !this->hayUnderflow(techo,piso) ){
		if(this->debug) cout << "resolverUnderflow: No hay Underflow." << endl;
		return 0;
	}
	uChar cont = 1;
	
	uChar nuevo_techo = 0x1;
	uChar nuevo_piso  = 0x0;
	
	// Cuento la cantidad de underflow 
	uChar mask = 0b00100000;
	uChar offset;
	for( offset = 3 ; offset<9 ; offset++ ){
		if( ( ~techo == (~techo | mask) ) and ( piso == (piso | mask) ) ){
			cont++;
		}else{
			break;
		}
		mask >>= 1;
	}
	
	this->contador_underflow += cont;
	
	if(this->debug) {
		cout << "resolverUnderflow: offset: " << (int)offset << endl;
		cout << "resolverUnderflow: Cantidad de underflow: " << (int)cont << endl;
		cout << "resolverUnderflow: Cantidad de underflow acumulado: " << (int)this->contador_underflow << endl;
	}
	
	// Agrego, si los hay, los bits que no dieron 
	// underflow al nuevo techo y nuevo piso
	while( offset++ < 9 ){
		nuevo_techo <<= 1;
		if( techo == (techo | mask) ){
			nuevo_techo |= 0x01;
		}
		
		nuevo_piso <<= 1;
		if( piso == (piso | mask) ){
			nuevo_piso |= 0x01;
		}
		
		mask >>= 1;
	}
	
	// Agrego el padding para el techo (con bits en 1) y
	// para el piso (con bits en 0)
	while( cont-- > 0 ){
		
		nuevo_techo <<= 1;
		nuevo_techo |= 0x01;
		
		nuevo_piso <<= 1;
	}
	
	this->intervalo.techo = nuevo_techo;
	this->intervalo.piso  = nuevo_piso;
	
	if(this->debug){
		cout << "resolverUnderflow: nuevo techo: " << (int)this->intervalo.techo << endl;
		cout << "resolverUnderflow: nuevo piso: " << (int)this->intervalo.piso << endl;
	}
	
	return 0;
}

void AritmeticoDecompress::setTablaChars( vector<char> vectorChars ){
	vector<char>::iterator it;
	CharFrec tupla;
	for( it = vectorChars.begin() ; it < vectorChars.end() ; it++ ){
		tupla.caracter   = *it;
		tupla.frecuencia = 1;
		this->intervalo.tblFrecuencias.push_back(tupla);
		this->contador_frecuencias++; 
	}

	this->intervalo.techo = 255;
	this->intervalo.piso = 0;
}



#endif /* ARITMETICO_H */
